import httpx
import json
import time
import uuid
from datetime import datetime
from app.models import OrdersList, CustomerUser
from app.schemas.orders import SendMessage
from app.settings import settings
from fastapi import HTTPException
from wechatpayv3 import WeChatPay, WeChatPayType
from app.utils.fxn import read_file
from app.log.log import logger

# 初始化微信支付
wxpay = WeChatPay(
    wechatpay_type=WeChatPayType.NATIVE,
    mchid=settings.WEICHAT_MCHID,
    private_key=read_file(settings.WEICHAT_KEY_PATH),
    cert_serial_no=settings.WEICHAT_NO,
    apiv3_key=settings.WEICHAT_API_V3_KEY,
    appid=settings.WEICHAT_APP_ID,
    notify_url=settings.WEICHAT_NOTIFY,
    partner_mode=False,
    proxy=None,
    timeout=(10, 30),
    public_key=read_file(settings.WEICHAT_PUBLIC_KEY),
    public_key_id=settings.WEICHAT_PUBLIC_KEYID
)

class WeChatController:
    def __init__(self):
        self.mchid = settings.WEICHAT_MCHID
        self.app_id = settings.WEICHAT_APP_ID
        self.app_secret = settings.WEICHAT_APP_SECRET
        self.serial_no = settings.WEICHAT_NO

    async def get_access_token(self) -> str:
        """ 获取小程序的 access_token """
        url = f'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={self.app_id}&secret={self.app_secret}'
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(url)
                response.raise_for_status()  # 检查请求是否成功
                data = response.json()
                return data['access_token']
            except httpx.HTTPStatusError as e:
                logger.error(f"HTTP错误: {e}")
                raise HTTPException(status_code=500, detail="获取access_token失败")
            except Exception as e:
                logger.error(f"获取access_token时发生错误: {e}")
                raise HTTPException(status_code=500, detail="获取access_token失败")

    async def send_subscription_message(self, access_token: str, openid: str, template_id: str, page: str, data: dict):
        """ 发送订阅消息 """
        payload = {
            "touser": openid,
            "template_id": template_id,
            "page": page,
            "data": data
        }
        url = f"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={access_token}"
        async with httpx.AsyncClient() as client:
            try:
                response = await client.post(url, json=payload)
                response.raise_for_status()  # 检查请求是否成功
                result = response.json()
                if result.get("errcode") != 0:
                    raise HTTPException(status_code=500, detail=f"发送消息失败: {result}")
                return result
            except httpx.HTTPStatusError as e:
                logger.error(f"HTTP错误: {e}")
                raise HTTPException(status_code=500, detail="发送消息失败")
            except Exception as e:
                logger.error(f"发送订阅消息时发生错误: {e}")
                raise HTTPException(status_code=500, detail="发送消息失败")

    async def send_order_message(self, data_in: SendMessage):
        """ 发送订单完成通知消息 """
        logger.info(f"发送订单完成通知消息")
        openid = data_in.openid
        try:
            if data_in.user_id > 0:
                user = await CustomerUser.filter(id=data_in.user_id).first().values("open_id")
                openid = user['open_id']

            order = await OrdersList.filter(id=data_in.order_id).first().values()
            if order["type"] == 1 and order["is_express"] == 1:
                access_token = await self.get_access_token()
                send_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                template_data = {
                    "character_string5": {"value": data_in.order_id},  # 订单号
                    "time3": {"value": send_time},  # 完成时间
                    "amount6": {"value": f"￥{order['price']:.2f}"},  # 订单金额
                    "phrase7": {"value": "接待完成"},
                    "thing4": {"value": "点单商品"}
                }
                template_id = "XPf2BNgchENiQSODOOiyG4H0t7HB5YCD_TnD1eIKxeA"
                page = f"/pages/shop/cash_register?order_id={data_in.order_id}"
                return await self.send_subscription_message(access_token, openid, template_id, page, template_data)
            return False
        except Exception as e:
            logger.error(f"发送订单消息失败: {e}")
            return False

    def pay(self, out_trade_no, total_fee, body, openid):
        """ 发起微信支付请求 """
        try:
            code, message = wxpay.pay(
                description=body,
                out_trade_no=out_trade_no,
                amount={'total': total_fee},
                pay_type=WeChatPayType.MINIPROG,
                payer={'openid': openid}
            )
            result = json.loads(message)
            logger.info(f"微信支付响应: {result}")
            if code in range(200, 300):
                prepay_id = result.get('prepay_id')
                timestamp = str(int(time.time()))
                noncestr = str(uuid.uuid4()).replace('-', '')
                package = f'prepay_id={prepay_id}'
                sign = wxpay.sign(data=[self.app_id, timestamp, noncestr, package])
                return {
                    "pay_status": 1,
                    "msg": "支付唤起成功",
                    'code': 0,
                    'result': {
                        'appId': self.app_id,
                        'timeStamp': timestamp,
                        'nonceStr': noncestr,
                        'package': package,
                        'signType': 'RSA',
                        'paySign': sign
                    }
                }
            else:
                return {"pay_status": 0, 'code': -1, 'msg': "支付唤起失败", 'result': {'reason': result.get('code')}}
        except Exception as e:
            logger.error(f"微信支付请求失败: {e}")
            return {"pay_status": 0, 'code': -1, 'msg': "支付唤起失败"}

    @staticmethod
    def notify(headers, data):
        """ 处理支付回调 """
        try:
            result = wxpay.callback(headers, data)
            if result and result.get('event_type') == 'TRANSACTION.SUCCESS':
                resp = result.get('resource')
                logger.info(f"开始回调进程")
                return {
                    "notify_status": 1,
                    "out_trade_no": resp.get('out_trade_no'),
                    "amount": resp.get('amount').get('total'),
                    "transaction_id": resp.get('transaction_id'),
                    "payer": resp.get('payer'),
                    "success_time": resp.get('success_time')
                }
            else:
                return {"notify_status": 0}
        except Exception as e:
            logger.error(f"支付回调处理失败: {e}")
            return {"notify_status": 0}

    @staticmethod
    def refund(out_refund_no, amount, transaction_id):
        """ 发起退款请求 """
        try:
            code, message = wxpay.refund(
                out_refund_no=out_refund_no,
                amount={'refund': int(amount), 'total': int(amount), 'currency': 'CNY'},
                transaction_id=transaction_id
            )
            result = json.loads(message)
            logger.info(f"微信退款响应: {result}")
            if code in range(200, 300):
                return {"refund_status": 1, 'msg': "微信退款成功"}
            else:
                return {"refund_status": 0, 'msg': "微信退款失败", 'result': {'reason': result.get('code')}}
        except Exception as e:
            logger.error(f"退款请求失败: {e}")
            return {"refund_status": 0, 'msg': "微信退款失败"}

wechat_controller = WeChatController()
